using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using DailyPoetryX.Models;
using DailyPoetryX.Util;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using Newtonsoft.Json;
using Xamarin.Essentials;

namespace DailyPoetryX.Services.Implementations {
    /// <summary>
    /// Azure收藏存储。
    /// </summary>
    public class AzureFavoriteStorage : IRemoteFavoriteStorage {
        /******** 公开变量 ********/

        /******** 私有变量 ********/

        /// <summary>
        /// 偏好存储。
        /// </summary>
        private IPreferenceStorage _preferenceStorage;

        /// <summary>
        /// 身份验证服务。
        /// </summary>
        private IAuthenticationService _authenticationService;

        /// <summary>
        /// 警告服务。
        /// </summary>
        private IAlertService _alertService;

        /// <summary>
        /// Token。
        /// </summary>
        private string _token;

        /// <summary>
        /// Token键。
        /// </summary>
        private const string TokenKey = nameof(AzureFavoriteStorage) + ".Token";

        /// <summary>
        /// 上一次ping。
        /// </summary>
        private DateTime _lastPing;

        /// <summary>
        /// 上一次Ping键。
        /// </summary>
        private const string LastPingKey =
            nameof(AzureFavoriteStorage) + ".LastPing";

        /// <summary>
        /// 服务端点。
        /// </summary>
        private const string Endpoint =
            "https://dailypoetryx.azurewebsites.net/api/";

        /// <summary>
        /// DailyPoetryX Azure服务器。
        /// </summary>
        private const string Server = "DailyPoetryX Azure服务器";

        /******** 继承方法 ********/

        /// <summary>
        /// 状态。
        /// </summary>
        public string Status {
            get => _status;
            set {
                _status = value;
                StatusChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        private string _status;

        /// <summary>
        /// 状态改变事件。
        /// </summary>
        public event EventHandler StatusChanged;

        /// <summary>
        /// 获得所有收藏项，包括收藏与非收藏。
        /// </summary>
        public async Task<IList<Favorite>> GetFavoriteItemsAsync() {
            Stream responseStream;
            using (var httpClient = new HttpClient()) {
                httpClient.DefaultRequestHeaders.Authorization =
                    new AuthenticationHeaderValue("Bearer", _token);

                HttpResponseMessage response;
                try {
                    response =
                        await httpClient.GetAsync(Endpoint + "GetFavoriteBlob");
                    response.EnsureSuccessStatusCode();
                } catch (Exception e) {
                    _alertService.ShowAlert(
                        ErrorMessages.HTTP_CLIENT_ERROR_TITLE,
                        ErrorMessages.HttpClientErrorMessage(Server, e.Message),
                        ErrorMessages.HTTP_CLIENT_ERROR_BUTTON);
                    return new List<Favorite>();
                }

                if (response.StatusCode == HttpStatusCode.NoContent) {
                    return new List<Favorite>();
                }

                responseStream = await response.Content.ReadAsStreamAsync();
            }

            var zipStream = new ZipInputStream(responseStream);
            var zipEntry = zipStream.GetNextEntry();

            if (zipEntry == null) {
                return new List<Favorite>();
            }

            var buffer = new byte[1024];
            var jsonStream = new MemoryStream();
            StreamUtils.Copy(zipStream, jsonStream, buffer);
            zipStream.Close();
            responseStream.Close();

            jsonStream.Position = 0;
            var jsonReader = new StreamReader(jsonStream);
            var favoriteList =
                JsonConvert.DeserializeObject<IList<Favorite>>(
                    await jsonReader.ReadToEndAsync());
            jsonReader.Close();
            jsonStream.Close();

            return favoriteList?.ToList() ?? new List<Favorite>();
        }

        /// <summary>
        /// 保存所有收藏项，包括收藏与非收藏。
        /// </summary>
        /// <param name="favoriteList">所有收藏项，包括收藏与非收藏。</param>
        public async Task<ServiceResult> SaveFavoriteItemsAsync(
            IList<Favorite> favoriteList) {
            Status = "正在压缩远程收藏项";

            var json = JsonConvert.SerializeObject(favoriteList);

            MemoryStream memoryStream = new MemoryStream();
            ZipOutputStream zipStream = new ZipOutputStream(memoryStream);
            zipStream.SetLevel(3);

            ZipEntry newEntry = new ZipEntry("DPX.json");
            newEntry.DateTime = DateTime.Now;
            zipStream.PutNextEntry(newEntry);

            var jsonStream = new MemoryStream(Encoding.UTF8.GetBytes(json));
            StreamUtils.Copy(jsonStream, zipStream, new byte[1024]);
            jsonStream.Close();
            zipStream.CloseEntry();

            zipStream.IsStreamOwner = false;
            zipStream.Close();

            Status = "正在上传远程收藏项";
            memoryStream.Position = 0;
            using (var httpClient = new HttpClient()) {
                httpClient.DefaultRequestHeaders.Authorization =
                    new AuthenticationHeaderValue("Bearer", _token);

                using (var content = new MultipartFormDataContent()) {
                    content.Add(new StreamContent(memoryStream),
                        "DailyPoetryX.zip", "DailyPoetryX.zip");

                    try {
                        var response = await httpClient.PutAsync(
                            Endpoint + "SaveFavoriteBlob", content);
                        response.EnsureSuccessStatusCode();
                    } catch (Exception e) {
                        _alertService.ShowAlert(
                            ErrorMessages.HTTP_CLIENT_ERROR_TITLE,
                            ErrorMessages.HttpClientErrorMessage(Server,
                                e.Message),
                            ErrorMessages.HTTP_CLIENT_ERROR_BUTTON);
                        return new ServiceResult {
                            Status = ServiceResultStatus.Exception,
                            Message = e.Message
                        };
                    } finally {
                        memoryStream.Close();
                    }
                }
            }

            return new ServiceResult {Status = ServiceResultStatus.Ok};
        }

        /// <summary>
        /// 是否已登录。
        /// </summary>
        public async Task<bool> IsSignedInAsync() {
            if (string.IsNullOrWhiteSpace(_token)) {
                _token = await SecureStorage.GetAsync(TokenKey);
            }

            if (string.IsNullOrWhiteSpace(_token)) {
                return false;
            }

            if (_lastPing == null) {
                _lastPing =
                    _preferenceStorage.Get(LastPingKey, DateTime.MinValue);
            }

            if (_lastPing.AddHours(6) > DateTime.Now) {
                return true;
            }

            string message;
            using (var httpClient = new HttpClient()) {
                httpClient.DefaultRequestHeaders.Authorization =
                    new AuthenticationHeaderValue("Bearer", _token);

                HttpResponseMessage response;
                try {
                    response = await httpClient.GetAsync(Endpoint + "Ping");
                    response.EnsureSuccessStatusCode();
                } catch (Exception e) {
                    _alertService.ShowAlert(
                        ErrorMessages.HTTP_CLIENT_ERROR_TITLE,
                        ErrorMessages.HttpClientErrorMessage(Server, e.Message),
                        ErrorMessages.HTTP_CLIENT_ERROR_BUTTON);
                    return false;
                }

                message = await response.Content.ReadAsStringAsync();
            }

            if (message != "Pong") {
                _alertService.ShowAlert(ErrorMessages.HTTP_CLIENT_ERROR_TITLE,
                    $"Ping {Server}时未能获得期望的响应：${message}",
                    ErrorMessages.HTTP_CLIENT_ERROR_BUTTON);
                return false;
            }

            _lastPing = DateTime.Now;
            _preferenceStorage.Set(LastPingKey, _lastPing);

            return true;
        }

        /// <summary>
        /// 登录。
        /// </summary>
        public async Task<bool> SignInAsync() {
            Status = "正在登录DailyPoetryX Azure服务器";

            var authenticationResult =
                await _authenticationService.Authenticate();

            if (authenticationResult.IsError) {
                return false;
            }

            _token = authenticationResult.AccessToken;
            _lastPing = DateTime.Now;

            await SecureStorage.SetAsync(TokenKey, _token);
            _preferenceStorage.Set(LastPingKey, _lastPing);

            return true;
        }

        /// <summary>
        /// 注销。
        /// </summary>
        public async Task SignOutAsync() {
            Status = "正在退出DailyPoetry X Azure服务器登录";

            _token = "";
            _lastPing = DateTime.MinValue;

            SecureStorage.Remove(TokenKey);
            _preferenceStorage.Remove(LastPingKey);
        }

        /******** 公开方法 ********/

        /// <summary>
        /// Azure收藏存储。
        /// </summary>
        /// <param name="preferenceStorage">偏好存储。</param>
        /// <param name="authenticationService">身份验证服务。</param>
        /// <param name="alertService">警告服务。</param>
        public AzureFavoriteStorage(IPreferenceStorage preferenceStorage,
            IAuthenticationService authenticationService,
            IAlertService alertService) {
            _preferenceStorage = preferenceStorage;
            _authenticationService = authenticationService;
            _alertService = alertService;
        }

        /******** 私有方法 ********/
    }
}